using System;
using System.Collections.Generic;
using System.Text;
using System.Web.Security;
using FreeLancer.HRB.Services;
using System.Collections.Specialized;
using System.Configuration.Provider;
using FreeLancer.HRB.Domains;

namespace FreeLancer.HRB.Core.Security
{
  /// <summary>
  /// Generic Db Role Provider
  /// </summary>
  public class DbRoleProvider : RoleProvider
  {
    private RolesService _roleService = new RolesService();
    private UsersService _userService = new UsersService();
    private UserRolesService _userRoleService = new UserRolesService();

    private string _applicationName;

    /// <summary>
    /// Initializes the provider
    /// </summary>
    /// <param name="name">Configuration name</param>
    /// <param name="config">Configuration settings</param>
    public override void Initialize(string name, NameValueCollection config)
    {
      if (config == null)
      {
        throw new ArgumentNullException("config");
      }

      if (String.IsNullOrEmpty(name))
      {
        name = "DbRoleProvider";
      }

      if (String.IsNullOrEmpty(config["description"]))
      {
        config.Remove("description");
        config.Add("description", "Generic Database Membership Provider");
      }

      base.Initialize(name, config);

      if (config["applicationName"] == null)
      {
        // default to BlogEngine
        config["applicationName"] = "HRB";
      }
      _applicationName = config["applicationName"];
      config.Remove("applicationName");

      // Throw an exception if unrecognized attributes remain
      if (config.Count > 0)
      {
        string attr = config.GetKey(0);
        if (!String.IsNullOrEmpty(attr))
          throw new ProviderException("Unrecognized attribute: " + attr);
      }
    }

    /// <summary>
    /// Adds all users in user array to all roles in role array.
    /// </summary>
    /// <param name="usernames">Usernames.</param>
    /// <param name="roleNames">Role names.</param>
    public override void AddUsersToRoles(string[] usernames, string[] roleNames)
    {
      TList<FreeLancer.HRB.Domains.Roles> roles = new TList<FreeLancer.HRB.Domains.Roles>();
      foreach (string rolename in roleNames)
      {
        FreeLancer.HRB.Domains.Roles role = GetRoleByRoleName(rolename);
        if(role != null)
          roles.Add(role);
      }

      using (ConnectionScope.CreateTransaction())
      {
        foreach (string username in usernames)
        {
          Users user = GetUserByUserName(username);
          if (user != null)
          {
            foreach (FreeLancer.HRB.Domains.Roles role in roles)
            {
              UserRoles userRole = new UserRoles();
              userRole.RoleId = role.RoleId;
              userRole.UserId = user.UserId;

              _userRoleService.Save(userRole);
            }
          }
        }

        ConnectionScope.Complete();
      }
    }

    /// <summary>
    /// Returns the application name as set in the web.config
    /// otherwise returns HRB.  Set will throw an error.
    /// </summary>
    public override string ApplicationName
    {
      get { return _applicationName; }
      set { throw new NotImplementedException(); }
    }

    /// <summary>
    /// Adds a new role to the database.
    /// </summary>
    /// <param name="roleName">Role name.</param>
    public override void CreateRole(string roleName)
    {
      if (GetRoleByRoleName(roleName) != null)
      {
        throw new ArgumentException("Role already exists.");
      }

      FreeLancer.HRB.Domains.Roles role = new FreeLancer.HRB.Domains.Roles();
      role.Role = roleName;

      using (ConnectionScope.CreateTransaction())
      {
        _roleService.Save(role);
        ConnectionScope.Complete();
      }
    }

    /// <summary>
    /// Removes a role from database.
    /// </summary>
    /// <param name="roleName">Role name.</param>
    /// <param name="throwOnPopulatedRole">Throw on populatedRole.</param>
    /// <returns>Delete successfully.</returns>
    public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
    {
      bool success = false;
      if (roleName != "Administrators")
      {
        FreeLancer.HRB.Domains.Roles role = GetRoleByRoleName(roleName);
        if (role != null)
        {
          using (ConnectionScope.CreateTransaction())
          {
            _roleService.Delete(role);
            success = ConnectionScope.Complete();
          }
        }
      }

      return success;
    }

    /// <summary>
    /// Returns all users in selected role with names that match usernameToMatch
    /// </summary>
    /// <param name="roleName">Role name</param>
    /// <param name="usernameToMatch">Username to match</param>
    /// <returns>Usernames</returns>
    public override string[] FindUsersInRole(string roleName, string usernameToMatch)
    {
      FreeLancer.HRB.Domains.Roles role = GetRoleByRoleName(roleName);
      List<string> usernames = new List<string>();
      if (role != null)
      {
        int total = 0;
        TList<Users> users = _userService.GetPaged("UserName LIKE '" + usernameToMatch + "%'", string.Empty, 0, 1000, out total);

        foreach (Users user in users)
          usernames.Add(user.UserName);
      }

      return usernames.ToArray();
    }

    /// <summary>
    /// Returns array of all roles in database
    /// </summary>
    /// <returns>Role names</returns>
    public override string[] GetAllRoles()
    {
      TList<FreeLancer.HRB.Domains.Roles> roles = _roleService.GetAll();
      List<string> roleNames = new List<string>(roles.Count);

      foreach (FreeLancer.HRB.Domains.Roles role in roles)
      {
        roleNames.Add(role.Role);
      }

      return roleNames.ToArray();
    }

    /// <summary>
    /// Return an array of roles that user is in.
    /// </summary>
    /// <param name="username">Username</param>
    /// <returns>Role names</returns>
    public override string[] GetRolesForUser(string username)
    {
      Users user = GetUserByUserName(username);
      TList<UserRoles> userRoles = _userRoleService.GetByUserId(user.UserId);
      List<string> roleStrs = new List<string>(userRoles.Count);

      foreach (UserRoles userRole in userRoles)
      {
        FreeLancer.HRB.Domains.Roles role = _roleService.GetByRoleId(userRole.RoleId);
        if(role != null)
          roleStrs.Add(role.Role);
      }

      return roleStrs.ToArray();
    }

    /// <summary>
    /// Returns array of users in selected role.
    /// </summary>
    /// <param name="roleName">Role name.</param>
    /// <returns>Users in role.</returns>
    public override string[] GetUsersInRole(string roleName)
    {
      FreeLancer.HRB.Domains.Roles role = GetRoleByRoleName(roleName);
      List<string> userNames = new List<string>();

      if (role != null)
      {
        TList<UserRoles> userRoles = _userRoleService.GetByRoleId(role.RoleId);
        foreach (UserRoles userRole in userRoles)
        {
          Users user = _userService.GetByUserId(userRole.UserId);
          if (user != null)
            userNames.Add(user.UserName);
        }
      }

      return userNames.ToArray();
    }

    /// <summary>
    /// Check to see if user is in a role.
    /// </summary>
    /// <param name="username">Username.</param>
    /// <param name="roleName">Role name.</param>
    /// <returns>Whether user is in role.</returns>
    public override bool IsUserInRole(string username, string roleName)
    {
      Users user = GetUserByUserName(username);
      FreeLancer.HRB.Domains.Roles role = GetRoleByRoleName(roleName);

      bool isUserInRole = false;

      if ((user != null) && (role != null))
      {
        TList<UserRoles> userRoles = _userRoleService.GetByUserId(user.UserId);
        foreach (UserRoles userRole in userRoles)
        {
          if ((userRole.UserId == user.UserId) && (userRole.RoleId == role.RoleId))
            isUserInRole = true;
        }
      }

      return isUserInRole;
    }

    /// <summary>
    /// Removes all users in user array from all roles in role array.
    /// </summary>
    /// <param name="usernames">Usernames.</param>
    /// <param name="roleNames">Role names.</param>
    public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
    {
      TList<FreeLancer.HRB.Domains.Roles> roles = new TList<FreeLancer.HRB.Domains.Roles>();
      foreach (string rolename in roleNames)
      {
        FreeLancer.HRB.Domains.Roles role = GetRoleByRoleName(rolename);
        if (role != null)
          roles.Add(role);
      }

      using (ConnectionScope.CreateTransaction())
      {
        foreach (string username in usernames)
        {
          Users user = GetUserByUserName(username);
          if (user == null)
            continue;

          TList<UserRoles> userRoles = _userRoleService.GetByUserId(user.UserId);

          foreach (UserRoles userRole in userRoles)
          {
            foreach(FreeLancer.HRB.Domains.Roles role in roles)
            {
              if ((user.UserId == userRole.UserId) && (role.RoleId == userRole.RoleId))
                _userRoleService.Delete(userRole.RoleId);
            }
          }
        }

        ConnectionScope.Complete();
      }
    }

    /// <summary>
    /// Checks to see if role exists
    /// </summary>
    /// <param name="roleName">Role name.</param>
    /// <returns>Whether role exists</returns>
    public override bool RoleExists(string roleName)
    {
      return (GetRoleByRoleName(roleName) != null);
    }

    private Users GetUserByUserName(string userName)
    {
      int total = 0;
      Users user = null;
      TList<Users> users = _userService.GetPaged("UserName='" + userName + "'", string.Empty, 0, 1, out total);

      if (users.Count > 0)
      {
        user = users[0];
      }

      return user;
    }

    private FreeLancer.HRB.Domains.Roles GetRoleByRoleName(string roleName)
    {
      int total = 0;
      FreeLancer.HRB.Domains.Roles role = null;
      TList<FreeLancer.HRB.Domains.Roles> roles = _roleService.GetPaged("Role='" + roleName + "'", string.Empty, 0, 1, out total);

      if (roles.Count > 0)
      {
        role = roles[0];
      }

      return role;
    }
  }
}
